/*
 * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.interceptors;

import java.util.HashMap;

import org.omg.CORBA.Any;
import org.omg.CORBA.BAD_INV_ORDER;
import org.omg.CORBA.BAD_PARAM;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.Context;
import org.omg.CORBA.ContextList;
import org.omg.CORBA.CTX_RESTRICT_SCOPE;
import org.omg.CORBA.ExceptionList;
import org.omg.CORBA.LocalObject;
import org.omg.CORBA.NamedValue;
import org.omg.CORBA.NO_IMPLEMENT;
import org.omg.CORBA.NO_RESOURCES;
import org.omg.CORBA.NVList;
import org.omg.CORBA.Object;
import org.omg.CORBA.ParameterMode;
import org.omg.CORBA.Policy;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.UserException;
import org.omg.CORBA.portable.ApplicationException;
import org.omg.CORBA.portable.InputStream;
import com.sun.corba.se.spi.servicecontext.ServiceContexts;
import com.sun.corba.se.spi.servicecontext.UnknownServiceContext;

import org.omg.IOP.ServiceContext;
import org.omg.IOP.ServiceContextHelper;
import org.omg.IOP.TaggedProfile;
import org.omg.IOP.TaggedProfileHelper;
import org.omg.IOP.TaggedComponent;
import org.omg.IOP.TaggedComponentHelper;
import org.omg.IOP.TAG_INTERNET_IOP;
import org.omg.Dynamic.Parameter;
import org.omg.PortableInterceptor.ClientRequestInfo;
import org.omg.PortableInterceptor.LOCATION_FORWARD;
import org.omg.PortableInterceptor.SUCCESSFUL;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;
import org.omg.PortableInterceptor.TRANSPORT_RETRY;
import org.omg.PortableInterceptor.USER_EXCEPTION;

import com.sun.corba.se.pept.protocol.MessageMediator;

import com.sun.corba.se.spi.ior.IOR;
import com.sun.corba.se.spi.ior.iiop.IIOPProfileTemplate;
import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.protocol.CorbaMessageMediator;
import com.sun.corba.se.spi.protocol.RetryType;
import com.sun.corba.se.spi.transport.CorbaContactInfo;
import com.sun.corba.se.spi.transport.CorbaContactInfoList;
import com.sun.corba.se.spi.transport.CorbaContactInfoListIterator;

import com.sun.corba.se.impl.encoding.CDROutputStream;
import com.sun.corba.se.impl.encoding.CDRInputStream_1_0;
import com.sun.corba.se.impl.orbutil.ORBUtility;
import com.sun.corba.se.impl.protocol.CorbaInvocationInfo;
import com.sun.corba.se.impl.util.RepositoryId;

/**
 * Implementation of the ClientRequestInfo interface as specified in
 * orbos/99-12-02 section 5.4.2.
 */
public final class ClientRequestInfoImpl
    extends RequestInfoImpl
    implements ClientRequestInfo {

  // The available constants for startingPointCall
  static final int CALL_SEND_REQUEST = 0;
  static final int CALL_SEND_POLL = 1;

  // The available constants for endingPointCall
  static final int CALL_RECEIVE_REPLY = 0;
  static final int CALL_RECEIVE_EXCEPTION = 1;
  static final int CALL_RECEIVE_OTHER = 2;

  //////////////////////////////////////////////////////////////////////
  //
  // NOTE: IF AN ATTRIBUTE IS ADDED, PLEASE UPDATE RESET();
  //
  //////////////////////////////////////////////////////////////////////

  // The current retry request status.  True if this request is being
  // retried and this info object is to be reused, or false otherwise.
  private RetryType retryRequest;

  // The number of times this info object has been (re)used.  This is
  // incremented every time a request is retried, and decremented every
  // time a request is complete.  When this reaches zero, the info object
  // is popped from the ClientRequestInfoImpl ThreadLocal stack in the ORB.
  private int entryCount = 0;

  // The RequestImpl is set when the call is DII based.
  // The DII query calls like ParameterList, ExceptionList,
  // ContextList will be delegated to RequestImpl.
  private org.omg.CORBA.Request request;

  // Sources of client request information
  private boolean diiInitiate;
  private CorbaMessageMediator messageMediator;

  // Cached information:
  private org.omg.CORBA.Object cachedTargetObject;
  private org.omg.CORBA.Object cachedEffectiveTargetObject;
  private Parameter[] cachedArguments;
  private TypeCode[] cachedExceptions;
  private String[] cachedContexts;
  private String[] cachedOperationContext;
  private String cachedReceivedExceptionId;
  private Any cachedResult;
  private Any cachedReceivedException;
  private TaggedProfile cachedEffectiveProfile;
  // key = Integer, value = IOP.ServiceContext.
  private HashMap cachedRequestServiceContexts;
  // key = Integer, value = IOP.ServiceContext.
  private HashMap cachedReplyServiceContexts;
  // key = Integer, value = TaggedComponent
  private HashMap cachedEffectiveComponents;


  protected boolean piCurrentPushed;

  //////////////////////////////////////////////////////////////////////
  //
  // NOTE: IF AN ATTRIBUTE IS ADDED, PLEASE UPDATE RESET();
  //
  //////////////////////////////////////////////////////////////////////

  /**
   * Reset the info object so that it can be reused for a retry,
   * for example.
   */
  void reset() {
    super.reset();

    // Please keep these in the same order that they're declared above.

    // 6763340
    retryRequest = RetryType.NONE;

    // Do not reset entryCount because we need to know when to pop this
    // from the stack.

    request = null;
    diiInitiate = false;
    messageMediator = null;

    // Clear cached attributes:
    cachedTargetObject = null;
    cachedEffectiveTargetObject = null;
    cachedArguments = null;
    cachedExceptions = null;
    cachedContexts = null;
    cachedOperationContext = null;
    cachedReceivedExceptionId = null;
    cachedResult = null;
    cachedReceivedException = null;
    cachedEffectiveProfile = null;
    cachedRequestServiceContexts = null;
    cachedReplyServiceContexts = null;
    cachedEffectiveComponents = null;

    piCurrentPushed = false;

    startingPointCall = CALL_SEND_REQUEST;
    endingPointCall = CALL_RECEIVE_REPLY;

  }

    /*
     **********************************************************************
     * Access protection
     **********************************************************************/

  // Method IDs for all methods in ClientRequestInfo.  This allows for a
  // convenient O(1) lookup for checkAccess().
  protected static final int MID_TARGET = MID_RI_LAST + 1;
  protected static final int MID_EFFECTIVE_TARGET = MID_RI_LAST + 2;
  protected static final int MID_EFFECTIVE_PROFILE = MID_RI_LAST + 3;
  protected static final int MID_RECEIVED_EXCEPTION = MID_RI_LAST + 4;
  protected static final int MID_RECEIVED_EXCEPTION_ID = MID_RI_LAST + 5;
  protected static final int MID_GET_EFFECTIVE_COMPONENT = MID_RI_LAST + 6;
  protected static final int MID_GET_EFFECTIVE_COMPONENTS
      = MID_RI_LAST + 7;
  protected static final int MID_GET_REQUEST_POLICY = MID_RI_LAST + 8;
  protected static final int MID_ADD_REQUEST_SERVICE_CONTEXT
      = MID_RI_LAST + 9;

  // ClientRequestInfo validity table (see ptc/00-08-06 table 21-1).
  // Note: These must be in the same order as specified in contants.
  private static final boolean validCall[][] = {
      // LEGEND:
      // s_req = send_request     r_rep = receive_reply
      // s_pol = send_poll        r_exc = receive_exception
      //                          r_oth = receive_other
      //
      // A true value indicates call is valid at specified point.
      // A false value indicates the call is invalid.
      //
      //
      // NOTE: If the order or number of columns change, update
      // checkAccess() accordingly.
      //
      //                              { s_req, s_pol, r_rep, r_exc, r_oth }
      // RequestInfo methods:
        /*request_id*/                  {true, true, true, true, true},
        /*operation*/                   {true, true, true, true, true},
        /*arguments*/                   {true, false, true, false, false},
        /*exceptions*/                  {true, false, true, true, true},
        /*contexts*/                    {true, false, true, true, true},
        /*operation_context*/           {true, false, true, true, true},
        /*result*/                      {false, false, true, false, false},
        /*response_expected*/           {true, true, true, true, true},
        /*sync_scope*/                  {true, false, true, true, true},
        /*reply_status*/                {false, false, true, true, true},
        /*forward_reference*/           {false, false, false, false, true},
        /*get_slot*/                    {true, true, true, true, true},
        /*get_request_service_context*/ {true, false, true, true, true},
        /*get_reply_service_context*/   {false, false, true, true, true},
      //
      // ClientRequestInfo methods::
        /*target*/                      {true, true, true, true, true},
        /*effective_target*/            {true, true, true, true, true},
        /*effective_profile*/           {true, true, true, true, true},
        /*received_exception*/          {false, false, false, true, false},
        /*received_exception_id*/       {false, false, false, true, false},
        /*get_effective_component*/     {true, false, true, true, true},
        /*get_effective_components*/    {true, false, true, true, true},
        /*get_request_policy*/          {true, false, true, true, true},
        /*add_request_service_context*/ {true, false, false, false, false}
  };


    /*
     **********************************************************************
     * Public ClientRequestInfo interfaces
     **********************************************************************/

  /**
   * Creates a new ClientRequestInfo implementation.
   * The constructor is package scope since no other package need create
   * an instance of this class.
   */
  protected ClientRequestInfoImpl(ORB myORB) {
    super(myORB);
    startingPointCall = CALL_SEND_REQUEST;
    endingPointCall = CALL_RECEIVE_REPLY;
  }

  /**
   * The object which the client called to perform the operation.
   */
  public org.omg.CORBA.Object target() {
    // access is currently valid for all states:
    //checkAccess( MID_TARGET );
    if (cachedTargetObject == null) {
      CorbaContactInfo corbaContactInfo = (CorbaContactInfo)
          messageMediator.getContactInfo();
      cachedTargetObject =
          iorToObject(corbaContactInfo.getTargetIOR());
    }
    return cachedTargetObject;
  }

  /**
   * The actual object on which the operation will be invoked.  If the
   * reply_status is LOCATION_FORWARD, then on subsequent requests,
   * effective_target will contain the forwarded IOR while target will
   * remain unchanged.
   */
  public org.omg.CORBA.Object effective_target() {
    // access is currently valid for all states:
    //checkAccess( MID_EFFECTIVE_TARGET );

    // Note: This is not necessarily the same as locatedIOR.
    // Reason: See the way we handle COMM_FAILURES in
    // ClientRequestDispatcher.createRequest, v1.32

    if (cachedEffectiveTargetObject == null) {
      CorbaContactInfo corbaContactInfo = (CorbaContactInfo)
          messageMediator.getContactInfo();
      // REVISIT - get through chain like getLocatedIOR helper below.
      cachedEffectiveTargetObject =
          iorToObject(corbaContactInfo.getEffectiveTargetIOR());
    }
    return cachedEffectiveTargetObject;
  }

  /**
   * The profile that will be used to send the request.  If a location
   * forward has occurred for this operation's object and that object's
   * profile change accordingly, then this profile will be that located
   * profile.
   */
  public TaggedProfile effective_profile() {
    // access is currently valid for all states:
    //checkAccess( MID_EFFECTIVE_PROFILE );

    if (cachedEffectiveProfile == null) {
      CorbaContactInfo corbaContactInfo = (CorbaContactInfo)
          messageMediator.getContactInfo();
      cachedEffectiveProfile =
          corbaContactInfo.getEffectiveProfile().getIOPProfile();
    }

    // Good citizen: In the interest of efficiency, we assume interceptors
    // will not modify the returned TaggedProfile in any way so we need
    // not make a deep copy of it.

    return cachedEffectiveProfile;
  }

  /**
   * Contains the exception to be returned to the client.
   */
  public Any received_exception() {
    checkAccess(MID_RECEIVED_EXCEPTION);

    if (cachedReceivedException == null) {
      cachedReceivedException = exceptionToAny(exception);
    }

    // Good citizen: In the interest of efficiency, we assume interceptors
    // will not modify the returned Any in any way so we need
    // not make a deep copy of it.

    return cachedReceivedException;
  }

  /**
   * The CORBA::RepositoryId of the exception to be returned to the client.
   */
  public String received_exception_id() {
    checkAccess(MID_RECEIVED_EXCEPTION_ID);

    if (cachedReceivedExceptionId == null) {
      String result = null;

      if (exception == null) {
        // Note: exception should never be null here since we will
        // throw a BAD_INV_ORDER if this is not called from
        // receive_exception.
        throw wrapper.exceptionWasNull();
      } else if (exception instanceof SystemException) {
        String name = exception.getClass().getName();
        result = ORBUtility.repositoryIdOf(name);
      } else if (exception instanceof ApplicationException) {
        result = ((ApplicationException) exception).getId();
      }

      // _REVISIT_ We need to be able to handle a UserException in the
      // DII case.  How do we extract the ID from a UserException?

      cachedReceivedExceptionId = result;
    }

    return cachedReceivedExceptionId;
  }

  /**
   * Returns the IOP::TaggedComponent with the given ID from the profile
   * selected for this request.  IF there is more than one component for a
   * given component ID, it is undefined which component this operation
   * returns (get_effective_component should be called instead).
   */
  public TaggedComponent get_effective_component(int id) {
    checkAccess(MID_GET_EFFECTIVE_COMPONENT);

    return get_effective_components(id)[0];
  }

  /**
   * Returns all the tagged components with the given ID from the profile
   * selected for this request.
   */
  public TaggedComponent[] get_effective_components(int id) {
    checkAccess(MID_GET_EFFECTIVE_COMPONENTS);
    Integer integerId = new Integer(id);
    TaggedComponent[] result = null;
    boolean justCreatedCache = false;

    if (cachedEffectiveComponents == null) {
      cachedEffectiveComponents = new HashMap();
      justCreatedCache = true;
    } else {
      // Look in cache:
      result = (TaggedComponent[]) cachedEffectiveComponents.get(
          integerId);
    }

    // null could mean we cached null or not in cache.
    if ((result == null) &&
        (justCreatedCache ||
            !cachedEffectiveComponents.containsKey(integerId))) {
      // Not in cache.  Get it from the profile:
      CorbaContactInfo corbaContactInfo = (CorbaContactInfo)
          messageMediator.getContactInfo();
      IIOPProfileTemplate ptemp =
          (IIOPProfileTemplate) corbaContactInfo.getEffectiveProfile().
              getTaggedProfileTemplate();
      result = ptemp.getIOPComponents(myORB, id);
      cachedEffectiveComponents.put(integerId, result);
    }

    // As per ptc/00-08-06, section 21.3.13.6., If not found, raise
    // BAD_PARAM with minor code INVALID_COMPONENT_ID.
    if ((result == null) || (result.length == 0)) {
      throw stdWrapper.invalidComponentId(integerId);
    }

    // Good citizen: In the interest of efficiency, we will assume
    // interceptors will not modify the returned TaggedCompoent[], or
    // the TaggedComponents inside of it.  Otherwise, we would need to
    // clone the array and make a deep copy of its contents.

    return result;
  }

  /**
   * Returns the given policy in effect for this operation.
   */
  public Policy get_request_policy(int type) {
    checkAccess(MID_GET_REQUEST_POLICY);
    // _REVISIT_ Our ORB is not policy-based at this time.
    throw wrapper.piOrbNotPolicyBased();
  }

  /**
   * Allows interceptors to add service contexts to the request.
   * <p>
   * There is no declaration of the order of the service contexts.  They
   * may or may not appear in the order they are added.
   */
  public void add_request_service_context(ServiceContext service_context,
      boolean replace) {
    checkAccess(MID_ADD_REQUEST_SERVICE_CONTEXT);

    if (cachedRequestServiceContexts == null) {
      cachedRequestServiceContexts = new HashMap();
    }

    addServiceContext(cachedRequestServiceContexts,
        messageMediator.getRequestServiceContexts(),
        service_context, replace);
  }

  // NOTE: When adding a method, be sure to:
  // 1. Add a MID_* constant for that method
  // 2. Call checkAccess at the start of the method
  // 3. Define entries in the validCall[][] table for interception points.

    /*
     **********************************************************************
     * Public RequestInfo interfaces
     *
     * These are implemented here because they have differing
     * implementations depending on whether this is a client or a server
     * request info object.
     **********************************************************************/

  /**
   * See RequestInfoImpl for javadoc.
   */
  public int request_id() {
    // access is currently valid for all states:
    //checkAccess( MID_REQUEST_ID );
        /*
         * NOTE: The requestId in client interceptors is the same as the
         * GIOP request id.  This works because both interceptors and
         * request ids are scoped by the ORB on the client side.
         */
    return messageMediator.getRequestId();
  }

  /**
   * See RequestInfoImpl for javadoc.
   */
  public String operation() {
    // access is currently valid for all states:
    //checkAccess( MID_OPERATION );
    return messageMediator.getOperationName();
  }

  /**
   * See RequestInfoImpl for javadoc.
   */
  public Parameter[] arguments() {
    checkAccess(MID_ARGUMENTS);

    if (cachedArguments == null) {
      if (request == null) {
        throw stdWrapper.piOperationNotSupported1();
      }

      // If it is DII request then get the arguments from the DII req
      // and convert that into parameters.
      cachedArguments = nvListToParameterArray(request.arguments());
    }

    // Good citizen: In the interest of efficiency, we assume
    // interceptors will be "good citizens" in that they will not
    // modify the contents of the Parameter[] array.  We also assume
    // they will not change the values of the containing Anys.

    return cachedArguments;
  }

  /**
   * See RequestInfoImpl for javadoc.
   */
  public TypeCode[] exceptions() {
    checkAccess(MID_EXCEPTIONS);

    if (cachedExceptions == null) {
      if (request == null) {
        throw stdWrapper.piOperationNotSupported2();
      }

      // Get the list of exceptions from DII request data, If there are
      // no exceptions raised then this method will return null.
      ExceptionList excList = request.exceptions();
      int count = excList.count();
      TypeCode[] excTCList = new TypeCode[count];
      try {
        for (int i = 0; i < count; i++) {
          excTCList[i] = excList.item(i);
        }
      } catch (Exception e) {
        throw wrapper.exceptionInExceptions(e);
      }

      cachedExceptions = excTCList;
    }

    // Good citizen: In the interest of efficiency, we assume
    // interceptors will be "good citizens" in that they will not
    // modify the contents of the TypeCode[] array.  We also assume
    // they will not change the values of the containing TypeCodes.

    return cachedExceptions;
  }

  /**
   * See RequestInfoImpl for javadoc.
   */
  public String[] contexts() {
    checkAccess(MID_CONTEXTS);

    if (cachedContexts == null) {
      if (request == null) {
        throw stdWrapper.piOperationNotSupported3();
      }

      // Get the list of contexts from DII request data, If there are
      // no contexts then this method will return null.
      ContextList ctxList = request.contexts();
      int count = ctxList.count();
      String[] ctxListToReturn = new String[count];
      try {
        for (int i = 0; i < count; i++) {
          ctxListToReturn[i] = ctxList.item(i);
        }
      } catch (Exception e) {
        throw wrapper.exceptionInContexts(e);
      }

      cachedContexts = ctxListToReturn;
    }

    // Good citizen: In the interest of efficiency, we assume
    // interceptors will be "good citizens" in that they will not
    // modify the contents of the String[] array.

    return cachedContexts;
  }

  /**
   * See RequestInfoImpl for javadoc.
   */
  public String[] operation_context() {
    checkAccess(MID_OPERATION_CONTEXT);

    if (cachedOperationContext == null) {
      if (request == null) {
        throw stdWrapper.piOperationNotSupported4();
      }

      // Get the list of contexts from DII request data, If there are
      // no contexts then this method will return null.
      Context ctx = request.ctx();
      // _REVISIT_ The API for get_values is not compliant with the spec,
      // Revisit this code once it's fixed.
      // _REVISIT_ Our ORB doesn't support Operation Context, This code
      // will not be excerscised until it's supported.
      // The first parameter in get_values is the start_scope which
      // if blank makes it as a global scope.
      // The second parameter is op_flags which is set to RESTRICT_SCOPE
      // As there is only one defined in the spec.
      // The Third param is the pattern which is '*' requiring it to
      // get all the contexts.
      NVList nvList = ctx.get_values("", CTX_RESTRICT_SCOPE.value, "*");
      String[] context = new String[(nvList.count() * 2)];
      if ((nvList != null) && (nvList.count() != 0)) {
        // The String[] array will contain Name and Value for each
        // context and hence double the size in the array.
        int index = 0;
        for (int i = 0; i < nvList.count(); i++) {
          NamedValue nv;
          try {
            nv = nvList.item(i);
          } catch (Exception e) {
            return (String[]) null;
          }
          context[index] = nv.name();
          index++;
          context[index] = nv.value().extract_string();
          index++;
        }
      }

      cachedOperationContext = context;
    }

    // Good citizen: In the interest of efficiency, we assume
    // interceptors will be "good citizens" in that they will not
    // modify the contents of the String[] array.

    return cachedOperationContext;
  }

  /**
   * See RequestInfoImpl for javadoc.
   */
  public Any result() {
    checkAccess(MID_RESULT);

    if (cachedResult == null) {
      if (request == null) {
        throw stdWrapper.piOperationNotSupported5();
      }
      // Get the result from the DII request data.
      NamedValue nvResult = request.result();

      if (nvResult == null) {
        throw wrapper.piDiiResultIsNull();
      }

      cachedResult = nvResult.value();
    }

    // Good citizen: In the interest of efficiency, we assume that
    // interceptors will not modify the contents of the result Any.
    // Otherwise, we would need to create a deep copy of the Any.

    return cachedResult;
  }

  /**
   * See RequestInfoImpl for javadoc.
   */
  public boolean response_expected() {
    // access is currently valid for all states:
    //checkAccess( MID_RESPONSE_EXPECTED );
    return !messageMediator.isOneWay();
  }

  /**
   * See RequestInfoImpl for javadoc.
   */
  public Object forward_reference() {
    checkAccess(MID_FORWARD_REFERENCE);
    // Check to make sure we are in LOCATION_FORWARD
    // state as per ptc/00-08-06, table 21-1
    // footnote 2.
    if (replyStatus != LOCATION_FORWARD.value) {
      throw stdWrapper.invalidPiCall1();
    }

    // Do not cache this value since if an interceptor raises
    // forward request then the next interceptor in the
    // list should see the new value.
    IOR ior = getLocatedIOR();
    return iorToObject(ior);
  }

  private IOR getLocatedIOR() {
    IOR ior;
    CorbaContactInfoList contactInfoList = (CorbaContactInfoList)
        messageMediator.getContactInfo().getContactInfoList();
    ior = contactInfoList.getEffectiveTargetIOR();
    return ior;
  }

  protected void setLocatedIOR(IOR ior) {
    ORB orb = (ORB) messageMediator.getBroker();

    CorbaContactInfoListIterator iterator = (CorbaContactInfoListIterator)
        ((CorbaInvocationInfo) orb.getInvocationInfo())
            .getContactInfoListIterator();

    // REVISIT - this most likely causes reportRedirect to happen twice.
    // Once here and once inside the request dispatcher.
    iterator.reportRedirect(
        (CorbaContactInfo) messageMediator.getContactInfo(),
        ior);
  }

  /**
   * See RequestInfoImpl for javadoc.
   */
  public org.omg.IOP.ServiceContext get_request_service_context(int id) {
    checkAccess(MID_GET_REQUEST_SERVICE_CONTEXT);

    if (cachedRequestServiceContexts == null) {
      cachedRequestServiceContexts = new HashMap();
    }

    return getServiceContext(cachedRequestServiceContexts,
        messageMediator.getRequestServiceContexts(),
        id);
  }

  /**
   * does not contain an etry for that ID, BAD_PARAM with a minor code of
   * TBD_BP is raised.
   */
  public org.omg.IOP.ServiceContext get_reply_service_context(int id) {
    checkAccess(MID_GET_REPLY_SERVICE_CONTEXT);

    if (cachedReplyServiceContexts == null) {
      cachedReplyServiceContexts = new HashMap();
    }

    // In the event this is called from a oneway, we will have no
    // response object.
    //
    // In the event this is called after a IIOPConnection.purgeCalls,
    // we will have a response object, but that object will
    // not contain a header (which would hold the service context
    // container).  See bug 4624102.
    //
    // REVISIT: this is the only thing used
    // from response at this time.  However, a more general solution
    // would avoid accessing other parts of response's header.
    //
    // Instead of throwing a NullPointer, we will
    // "gracefully" handle these with a BAD_PARAM with minor code 25.

    try {
      ServiceContexts serviceContexts =
          messageMediator.getReplyServiceContexts();
      if (serviceContexts == null) {
        throw new NullPointerException();
      }
      return getServiceContext(cachedReplyServiceContexts,
          serviceContexts, id);
    } catch (NullPointerException e) {
      // REVISIT how this is programmed - not what it does.
      // See purge calls test.  The waiter is woken up by the
      // call to purge calls - but there is no reply containing
      // service contexts.
      throw stdWrapper.invalidServiceContextId(e);
    }
  }

  //
  // REVISIT
  // Override RequestInfoImpl connection to work in framework.
  //

  public com.sun.corba.se.spi.legacy.connection.Connection connection() {
    return (com.sun.corba.se.spi.legacy.connection.Connection)
        messageMediator.getConnection();
  }



    /*
     **********************************************************************
     * Package-scope interfaces
     **********************************************************************/

  protected void setInfo(MessageMediator messageMediator) {
    this.messageMediator = (CorbaMessageMediator) messageMediator;
    // REVISIT - so mediator can handle DII in subcontract.
    this.messageMediator.setDIIInfo(request);
  }

  /**
   * Set or reset the retry request flag.
   */
  void setRetryRequest(RetryType retryRequest) {
    this.retryRequest = retryRequest;
  }

  /**
   * Retrieve the current retry request status.
   */
  RetryType getRetryRequest() {
    // 6763340
    return this.retryRequest;
  }

  /**
   * Increases the entry count by 1.
   */
  void incrementEntryCount() {
    this.entryCount++;
  }

  /**
   * Decreases the entry count by 1.
   */
  void decrementEntryCount() {
    this.entryCount--;
  }

  /**
   * Retrieve the current entry count
   */
  int getEntryCount() {
    return this.entryCount;
  }

  /**
   * Overridden from RequestInfoImpl.  Calls the super class, then
   * sets the ending point call depending on the reply status.
   */
  protected void setReplyStatus(short replyStatus) {
    super.setReplyStatus(replyStatus);
    switch (replyStatus) {
      case SUCCESSFUL.value:
        endingPointCall = CALL_RECEIVE_REPLY;
        break;
      case SYSTEM_EXCEPTION.value:
      case USER_EXCEPTION.value:
        endingPointCall = CALL_RECEIVE_EXCEPTION;
        break;
      case LOCATION_FORWARD.value:
      case TRANSPORT_RETRY.value:
        endingPointCall = CALL_RECEIVE_OTHER;
        break;
    }
  }

  /**
   * Sets DII request object in the RequestInfoObject.
   */
  protected void setDIIRequest(org.omg.CORBA.Request req) {
    request = req;
  }

  /**
   * Keeps track of whether initiate was called for a DII request.  The ORB
   * needs to know this so it knows whether to ignore a second call to
   * initiateClientPIRequest or not.
   */
  protected void setDIIInitiate(boolean diiInitiate) {
    this.diiInitiate = diiInitiate;
  }

  /**
   * See comment for setDIIInitiate
   */
  protected boolean isDIIInitiate() {
    return this.diiInitiate;
  }

  /**
   * The PICurrent stack should only be popped if it was pushed.
   * This is generally the case.  But exceptions which occur
   * after the stub's entry to _request but before the push
   * end up in _releaseReply which will try to pop unless told not to.
   */
  protected void setPICurrentPushed(boolean piCurrentPushed) {
    this.piCurrentPushed = piCurrentPushed;
  }

  protected boolean isPICurrentPushed() {
    return this.piCurrentPushed;
  }

  /**
   * Overridden from RequestInfoImpl.
   */
  protected void setException(Exception exception) {
    super.setException(exception);

    // Clear cached values:
    cachedReceivedException = null;
    cachedReceivedExceptionId = null;
  }

  protected boolean getIsOneWay() {
    return !response_expected();
  }

  /**
   * See description for RequestInfoImpl.checkAccess
   */
  protected void checkAccess(int methodID)
      throws BAD_INV_ORDER {
    // Make sure currentPoint matches the appropriate index in the
    // validCall table:
    int validCallIndex = 0;
    switch (currentExecutionPoint) {
      case EXECUTION_POINT_STARTING:
        switch (startingPointCall) {
          case CALL_SEND_REQUEST:
            validCallIndex = 0;
            break;
          case CALL_SEND_POLL:
            validCallIndex = 1;
            break;
        }
        break;
      case EXECUTION_POINT_ENDING:
        switch (endingPointCall) {
          case CALL_RECEIVE_REPLY:
            validCallIndex = 2;
            break;
          case CALL_RECEIVE_EXCEPTION:
            validCallIndex = 3;
            break;
          case CALL_RECEIVE_OTHER:
            validCallIndex = 4;
            break;
        }
        break;
    }

    // Check the validCall table:
    if (!validCall[methodID][validCallIndex]) {
      throw stdWrapper.invalidPiCall2();
    }
  }

}

// End of file.
